import {
  IDetectProfileOptionsPreference,
  INodePtyInstance,
  IPtyExitEvent,
  IPtyProcessChangeEvent,
  IShellLaunchConfig,
  ITerminalConnection,
  ITerminalError,
  ITerminalProfile,
  ITerminalService,
  ITerminalServiceClient,
  ITerminalServicePath,
  TERMINAL_ID_SEPARATOR,
  generateSessionId,
  isTerminalError,
} from '../common';

import {
  Disposable,
  Emitter,
  OperatingSystem,
  Event,
  Dispatcher,
} from '../../utils';
import { IApplicationService } from '../../common/application';

export class NodePtyTerminalService
  extends Disposable
  implements ITerminalService
{
  // 计数器用于生成唯一 sessionId
  static countId = 1;

  // 后端操作系统类型
  private backendOs: OperatingSystem | undefined;

  // 终端错误事件
  private _onError = this.registerDispose(new Emitter<ITerminalError>());
  public onError: Event<ITerminalError> = this._onError.event;

  // 终端退出事件
  private _onExit = this.registerDispose(new Emitter<IPtyExitEvent>());
  public onExit: Event<IPtyExitEvent> = this._onExit.event;

  // 进程变更事件
  private _onProcessChange = this.registerDispose(
    new Emitter<IPtyProcessChangeEvent>()
  );
  public onProcessChange = this._onProcessChange.event;

  // 数据分发器
  private _onDataDispatcher = this.registerDispose(new Dispatcher<string>());
  // 退出分发器
  private _onExitDispatcher = this.registerDispose(
    new Dispatcher<{
      code?: number;
      signal?: number;
    }>()
  );

  constructor(
    private readonly applicationService: IApplicationService,
    protected readonly serviceClientRPC: ITerminalServiceClient
  ) {
    super();
  }

  // 生成唯一的 sessionId
  async generateSessionId(): Promise<string> {
    return (
      this.applicationService.clientId +
      TERMINAL_ID_SEPARATOR +
      generateSessionId()
    );
  }

  // 检查终端 id 是否有效
  async check(ids: string[]) {
    const ensureResult = await this.serviceClientRPC.ensureTerminal(ids);
    return ensureResult;
  }

  // 创建自定义 WebSocket 连接对象
  private _createCustomWebSocket = (
    sessionId: string,
    pty: INodePtyInstance
  ): ITerminalConnection => ({
    name: pty.name,
    readonly: false,
    // onData: (handler: (value: string | ArrayBuffer) => void) =>
    //   this._onDataDispatcher.on(sessionId)(handler),
    // onExit: (handler: (exitCode: number | undefined) => void) =>
    //   this._onExitDispatcher.on(sessionId)((e) => {
    //     handler(e.code);
    //   }),
    // sendData: (message: string) => {
    //   this.sendText(sessionId, message);
    // },
    ptyInstance: pty,
    sessionId: sessionId,
  });

  // 通过启动配置创建并附加终端
  async attachByLaunchConfig(
    sessionId: string,
    cols: number,
    rows: number,
    launchConfig: IShellLaunchConfig
    // _xterm: XTerm
  ) {
    // 如果 launchConfig.executable 未设置，则使用默认 shell
    if (!launchConfig.executable) {
      launchConfig.executable = await this.getDefaultSystemShell();
    }

    const ptyInstance = await this.serviceClientRPC.create2(
      sessionId,
      cols,
      rows,
      launchConfig
    );

    if (ptyInstance && (ptyInstance.pid || ptyInstance.name)) {
      // 有 pid 或 name 的才视为创建成功
      // 创建不成功时会通过 closeClient 把错误信息传递回来
      const result = this._createCustomWebSocket(sessionId, ptyInstance);

      return result;
    }

    // 否则不返回（创建失败）
    return undefined;
  }

  // 发送消息到后端
  private _sendMessage(sessionId: string, json: any, requestId?: number) {
    const id = requestId || NodePtyTerminalService.countId++;

    this.serviceClientRPC.onMessage(
      sessionId,
      JSON.stringify({
        id,
        ...json,
      })
    );
  }

  // 发送文本到终端
  async sendText(sessionId: string, message: string | ArrayBuffer) {
    this._sendMessage(sessionId, {
      data: message,
    });
  }

  // 调整终端尺寸
  async resize(sessionId: string, cols: number, rows: number) {
    this._sendMessage(sessionId, {
      method: 'resize',
      params: { cols, rows },
    });
  }

  // 获取当前平台 key（osx/windows/linux）
  async getCodePlatformKey(): Promise<'osx' | 'windows' | 'linux'> {
    return await this.serviceClientRPC.getCodePlatformKey();
  }

  // 根据 sessionId 释放终端资源
  disposeById(sessionId: string) {
    this.serviceClientRPC.disposeById(sessionId);
  }

  // 获取终端进程 id
  async getProcessId(sessionId: string) {
    return this.serviceClientRPC.getProcessId(sessionId);
  }

  /**
   * for pty node
   *
   * @param sessionId
   * @param message
   */
  // 处理来自后端的消息
  onMessage(sessionId: string, message: string) {
    this._onDataDispatcher.dispatch(sessionId, message);
  }

  /**
   * for pty node
   *
   * @param sessionId
   */
  // 关闭终端客户端
  closeClient(id: string, code?: number, signal?: number): void;
  closeClient(
    sessionId: string,
    data: ITerminalError | { code?: number; signal?: number }
  ): void;
  closeClient(
    sessionId: string,
    data?: ITerminalError | { code?: number; signal?: number } | number,
    signal?: number
  ) {
    if (isTerminalError(data)) {
      this._onError.fire(data);
    } else if (typeof data === 'number') {
      // pty 正常退出
      this._onExitDispatcher.dispatch(sessionId, { code: data, signal });
      this._onExit.fire({ sessionId, code: data, signal });
    } else if (data) {
      // pty 正常退出
      this._onExitDispatcher.dispatch(sessionId, {
        code: data.code,
        signal: data.signal,
      });
      this._onExit.fire({ sessionId, code: data.code, signal: data.signal });
    }
  }

  // 进程变更通知
  $processChange(sessionId: string, processName: string) {
    this._onProcessChange.fire({ sessionId, processName });
  }

  // 获取后端操作系统类型
  async getOS() {
    if (this.backendOs) {
      return this.backendOs;
    }
    return (this.backendOs = this.serviceClientRPC.getOS());
  }

  // 获取终端当前工作目录
  async getCwd(sessionId: string) {
    try {
      return await this.serviceClientRPC.getCwd(sessionId);
    } catch {
      return undefined;
    }
  }

  // 获取系统默认 shell 路径
  async getDefaultSystemShell(): Promise<string> {
    return await this.serviceClientRPC.getDefaultSystemShell(
      await this.getOS()
    );
  }
}
